package de.hsmannheim.poolraumapp.model.fetcher;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.helper.StringUtil;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.google.common.base.CharMatcher;

import android.net.Uri;
import android.net.Uri.Builder;
import android.util.Log;

import de.hsmannheim.poolraumapp.model.Room;
import de.hsmannheim.poolraumapp.model.RoomReservation;
import de.hsmannheim.poolraumapp.model.RoomReservations;
import de.hsmannheim.poolraumapp.model.Rooms;


public class RoomReservationFetcher {
	private int MAX_QUERY_PARAMS = 50;
	
	private static final int TIMEOUT = 30000;
	private static final String TAG = RoomReservationFetcher.class.getName();
	
	private String rootUrl;
	
	public RoomReservationFetcher(String rootUrl) {
		this.rootUrl = rootUrl;
	}
	
	public RoomReservations fetch() throws RoomReservationFetchException {
		try {
			return new RoomReservations(new DateTime(DateTimeZone.UTC), fetchDepartments(rootUrl));
		} catch (IOException e) {
			throw new RoomReservationFetchException(e);
		}
	}

	private List<RoomReservation> fetchDepartments(String url) throws IOException, RoomReservationFetchException {
		Element select = getSelect(url);						
		Elements options = getOptions(select);
		
		String department = null;
		String previousDepartment = null;
		
		Uri rootUri = Uri.parse(rootUrl);
		Builder uriBuilder = rootUri.buildUpon();
		int paramCount = 0;
		List<RoomReservation> roomReservations = new ArrayList<RoomReservation>();
		Iterator<Element> optionsIterator = options.iterator();
		while (optionsIterator.hasNext()) {
			Element option = optionsIterator.next(); 
			department = option.text().substring(0, 1);
			if (paramCount >= MAX_QUERY_PARAMS || (previousDepartment != null && !department.equals(previousDepartment))) {
				//use previousDepartment
				fetchSemesters(uriBuilder.build().toString(), roomReservations, previousDepartment);
				uriBuilder = rootUri.buildUpon();
				paramCount = 0;
			}			
			if (!optionsIterator.hasNext()) {
				uriBuilder.appendQueryParameter(select.attr("name"), option.attr("value"));
				fetchSemesters(uriBuilder.build().toString(), roomReservations, department);
				break;
			}
			uriBuilder.appendQueryParameter(select.attr("name"), option.attr("value"));
			previousDepartment = department;
		}
		return roomReservations;
	}

	private void fetchSemesters(String url, List<RoomReservation> roomReservations, String department) throws RoomReservationFetchException, IOException {
		Log.i(TAG, "Fetching reservations for department " + department);
		Element select = getSelect(url);
		Elements options = getOptions(select);
		
		Uri rootUri = Uri.parse(rootUrl);
		Builder uriBuilder = rootUri.buildUpon();
		int paramCount = 0;	
		Iterator<Element> optionsIterator = options.iterator();
		while (optionsIterator.hasNext()) {
			uriBuilder.appendQueryParameter(select.attr("name"), optionsIterator.next().attr("value"));
			if (paramCount >= MAX_QUERY_PARAMS || !optionsIterator.hasNext()) {
				fetchCourses(uriBuilder.build().toString(), roomReservations, department);				
				uriBuilder = rootUri.buildUpon();
				paramCount = 0;
			}
		}
	}

	private void fetchCourses(String url, List<RoomReservation> roomReservations, String department) throws RoomReservationFetchException, IOException {
		Element select = getSelect(url);
		Elements options = getOptions(select);
		
		Uri rootUri = Uri.parse(rootUrl);
		Builder uriBuilder = rootUri.buildUpon();
		int paramCount = 0;	
		Iterator<Element> optionsIterator = options.iterator();
		while (optionsIterator.hasNext()) {
			uriBuilder.appendQueryParameter(select.attr("name"), optionsIterator.next().attr("value"));
			if (paramCount >= MAX_QUERY_PARAMS || !optionsIterator.hasNext()) {
				fetchReservations(uriBuilder.build().toString(), roomReservations, department);				
				uriBuilder = rootUri.buildUpon();
				paramCount = 0;
			}
		}
	}

	private void fetchReservations(String url, List<RoomReservation> roomReservations, String department) throws IOException {
		Document document = Jsoup.connect(url).timeout(TIMEOUT).get();
		fetchReservationsFromRows(document.getElementsByClass("row-even"), roomReservations, department);
		fetchReservationsFromRows(document.getElementsByClass("row-odd"), roomReservations, department);
	}

	private void fetchReservationsFromRows(Elements rows, List<RoomReservation> roomReservations, String department) {
		StringBuffer buffer = new StringBuffer();
		for (Element row : rows) {
			Elements cells = row.getElementsByTag("td");
			if (cells.size() > 0) {
				int block = Integer.parseInt(CharMatcher.is('0').trimLeadingFrom(cells.get(0).text().trim()));
				for (int dayOfWeek = 1; dayOfWeek < cells.size(); dayOfWeek++) {
					Elements elements = cells.get(dayOfWeek).children();
					buffer.setLength(0);
					Iterator<Element> elementIterator = elements.iterator();
					while (elementIterator.hasNext()) {
						Element element = elementIterator.next();
						if (element.tagName().equals("br") || !elementIterator.hasNext()) {
							int start = buffer.indexOf(" "); 
							int end = buffer.indexOf(" ", start + 1);
							if (end == -1) {
								end = buffer.length();
							}
							String room = buffer.substring(start, end).trim();
							//if (rooms.getRoom(room) != null) {
								String building = room.substring(0, 1);
								String roomNumber = room.substring(1);
								roomReservations.add(new RoomReservation(block, dayOfWeek, building, roomNumber, department));
							//}
							
							buffer.setLength(0);
						}
						else {
							buffer.append(element.text() + " ");
						}
					}
				}
			}
		}
	}

	private Elements getOptions(Element select) throws RoomReservationFetchException {
		return select.getElementsByTag("option");
	}

	private Element getSelect(String url) throws RoomReservationFetchException, IOException {
		Document document = Jsoup.connect(url).timeout(TIMEOUT).get();
		Elements selects = document.getElementsByTag("select");
		if (selects.size() > 1) {
			throw new RoomReservationFetchException("More than one <select> found in " + url);
		}
		else if (selects.size() < 1){
			throw new RoomReservationFetchException("No <select> found in " + url);
		}
		return selects.get(0);
	}	
}
